#include "in.h"

uint64_t FetchNewTransactionNo( struct MemDatabase *db )
{
	uint64_t	txn_no ;
	
	db->max_txn_no++;
	txn_no = db->max_txn_no ;
	
	return txn_no;
}

#if 0
struct MemTransactionData *GetUnusedTransactionData( struct MemDatabase *db )
{
	struct MemDataPage		*txn_datapage = NULL ;
	char				*txn_dataunit = NULL ;
	struct MemTransactionData	*txn_data = NULL ;
	
	if( tls_current_session.is_in_txn == 1 )
		return tls_current_session.p_txn_data;
	
	txn_datapage = list_last_entry_or_null( & (db->txn_datapage_list) , struct MemDataPage , datapage_list_node ) ;
	TRACE_PRINTF( "txn_datapage[%p] .dataunit_used_count[%zu] .dataunit_prealloc_count[%zu]\n" , txn_datapage , txn_datapage?txn_datapage->dataunit_used_count:0 , txn_datapage?txn_datapage->dataunit_prealloc_count:0 );
	if( txn_datapage == NULL || IsDataPageFull(txn_datapage) )
	{
		txn_datapage = CreateDataPage( db , sizeof(struct MemTransactionData) , & (db->txn_datapage_list) , & (db->txn_datapage_count) ) ;
		if( txn_datapage == NULL )
		{
			tls_last_errno = MDB_ERROR_ALLOC ;
			return NULL;
		}
		
		TRACE_PRINTF( "add txn-datapage[%p] to list , .dataunit_size[%zu]\n" , txn_datapage , txn_datapage->dataunit_size );
	}
	
	txn_data = (struct MemTransactionData *)GetUnusedDataUnit( txn_datapage ) ;
	TRACE_PRINTF( "GetUnusedDataUnit data[%p]\n" , txn_data );
	if( txn_dataunit == NULL )
		return NULL;
	
	txn_data->txn_no = FetchNewTransactionNo( db ) ;
	txn_data->db = db ;
	INIT_LIST_HEAD( & (txn_data->undo_list) );
	txn_data->executed_undo = NULL ;
	
	return txn_data;
}

void SetTransactionDataUnused( struct MemTransactionData *txn_data )
{
	struct MemDataPage		*txn_datapage = NULL ;
	
	txn_datapage = txn_data->txn_datapage ;
	
	TRACE_PRINTF( "SetDataUnitUnused undo_dataunit[%p]\n" , txn_data );
	SetDataUnitUnused( txn_datapage , (char*)txn_data );
	
	return;
}
#endif

int CreateCurrentTransaction( struct MemDatabase *db )
{
	if( db->is_in_txn == 1 )
		return 0;
	
	if( ! list_empty( & (db->txn.undo_list) ) )
		return (tls_last_errno=MDB_ERROR_IN_TRANSACTION);
	
	db->txn.txn_no = FetchNewTransactionNo( db ) ;
	TRACE_PRINTF( "db->txn.txn_no[%"PRIu64"]\n" , db->txn.txn_no );
	INIT_LIST_HEAD( & (db->txn.undo_list) );
	db->txn.executed_undo = NULL ;
	
	return 0;
}

int ExecuteCurrentTransaction( struct MemDatabase *db )
{
	struct MemUndoData		*undo_data = NULL ;
	struct MemUndoData		*next_undo_data = NULL ;
	struct MemRecordDataUnitHead	*record_dataunit_head = NULL ;
	
	int				nret = 0 ;
	
	TRACE_PRINTF( "db->txn.executed_undo[%p]\n" , db->txn.executed_undo );
	if( db->txn.executed_undo == NULL )
		db->txn.executed_undo = list_first_entry_or_null( & (db->txn.undo_list) , struct MemUndoData , undo_list_node ) ;
	else
		db->txn.executed_undo = list_next_entry( db->txn.executed_undo , struct MemUndoData , undo_list_node ) ;
	if( db->txn.executed_undo == NULL )
		return 0;
	
	TRACE_PRINTF( "begin ExecuteCurrentTransaction\n" );
	
	list_for_each_entry_safe_from( db->txn.executed_undo , next_undo_data , & (db->txn.undo_list) , struct MemUndoData , undo_list_node )
	{
		switch( db->txn.executed_undo->operation )
		{
			case MDB_UNDO_OPERATION_ADD_RECORD:
				TRACE_PRINTF( "MDB_UNDO_OPERATION_ADD_RECORD undo_data[%p] -> table_name[%s] new_record_dataunit[%p]\n" , db->txn.executed_undo , db->txn.executed_undo->table->table_name , db->txn.executed_undo->new_record_dataunit );
				db->txn.executed_undo->record_dataunit = GetUnusedRecordDataUnit( db->txn.executed_undo->table ) ;
				if( db->txn.executed_undo->record_dataunit == NULL )
				{
					TRACE_PRINTF( "GetUnusedRecordDataUnit table_name[%s] new_record_dataunit[%p] failed , tls_last_errno[%d]\n" , db->txn.executed_undo->table->table_name , db->txn.executed_undo->new_record_dataunit , tls_last_errno );
					db->txn.executed_undo = list_prev_entry_or_null( db->txn.executed_undo , & (db->txn.undo_list) , struct MemUndoData , undo_list_node ) ;
					return tls_last_errno;
				}
				memcpy( MDB_RECORD_DATAUNIT_TO_DATA(db->txn.executed_undo->record_dataunit) , MDB_RECORD_DATAUNIT_TO_DATA(db->txn.executed_undo->new_record_dataunit) , db->txn.executed_undo->table->record_size );
				if( db->txn.executed_undo->table->auto_increment_field.field_name )
					SetAutoIncrementFieldValue( db->txn.executed_undo->table , MDB_RECORD_DATAUNIT_TO_DATA(db->txn.executed_undo->record_dataunit) );
				record_dataunit_head = MDB_RECORD_DATAUNIT_TO_HEAD(db->txn.executed_undo->record_dataunit) ;
				record_dataunit_head->insert_txn_id = db->txn.txn_no ;
				record_dataunit_head->delete_txn_id = UINT64_MAX ;
				TRACE_PRINTF( "memcpy( record_dataunit[%p][%p] , new_record_dataunit[%p][%p] , record_dataunit_size[%zu] )" , db->txn.executed_undo->record_dataunit , MDB_RECORD_DATAUNIT_TO_DATA(db->txn.executed_undo->record_dataunit) , db->txn.executed_undo->new_record_dataunit , MDB_RECORD_DATAUNIT_TO_DATA(db->txn.executed_undo->new_record_dataunit) , sizeof(struct MemRecordDataUnitHead)+db->txn.executed_undo->table->record_size );
				TRACE_PRINTHEX( db->txn.executed_undo->record_dataunit , sizeof(struct MemRecordDataUnitHead)+db->txn.executed_undo->table->record_size );
				TRACE_PRINTEOL();
				break;
			case MDB_UNDO_OPERATION_BACKUP_AND_MODIFY_RECORD:
				TRACE_PRINTF( "MDB_UNDO_OPERATION_BACKUP_AND_MODIFY_RECORD undo_data[%p] -> ... table_name[%s] record_dataunit[%p] backup_record_dataunit[%p] new_record_dataunit[%p]\n" , db->txn.executed_undo , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->backup_record_dataunit , db->txn.executed_undo->new_record_dataunit );
				memcpy( MDB_RECORD_DATAUNIT_TO_DATA(db->txn.executed_undo->record_dataunit) , MDB_RECORD_DATAUNIT_TO_DATA(db->txn.executed_undo->new_record_dataunit) , db->txn.executed_undo->table->record_size );
				break;
			case MDB_UNDO_OPERATION_BACKUP_AND_REMOVE_RECORD:
				TRACE_PRINTF( "MDB_UNDO_OPERATION_BACKUP_AND_REMOVE_RECORD undo_data[%p] -> SetRecordDataUnitUnused table_name[%s] record_dataunit[%p] backup_record_dataunit[%p]\n" , db->txn.executed_undo , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->backup_record_dataunit );
				SetRecordDataUnitUnused( db->txn.executed_undo->record_dataunit );
				undo_data = list_prev_entry( db->txn.executed_undo , struct MemUndoData , undo_list_node ) ;
				list_for_each_entry_from_reverse( undo_data , & (db->txn.undo_list) , struct MemUndoData , undo_list_node )
				{
					if( undo_data->record_dataunit == db->txn.executed_undo->record_dataunit )
						undo_data->record_dataunit = db->txn.executed_undo->backup_record_dataunit ;
				}
				break;
			case MDB_UNDO_OPERATION_ADD_INDEX_ON_RECORD:
				if( db->txn.executed_undo->record_dataunit == NULL && db->txn.executed_undo->undo_data != NULL )
					db->txn.executed_undo->record_dataunit = db->txn.executed_undo->undo_data->record_dataunit ;
				TRACE_PRINTF( "MDB_UNDO_OPERATION_ADD_INDEX_ON_RECORD undo_data[%p] -> AddIndexDataOnRecord table_name[%s] record_dataunit[%p] index_name[%s]\n" , db->txn.executed_undo , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->index->index_name );
				nret = AddIndexDataOnRecord( db->txn.executed_undo->table , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->index ) ;
				if( nret )
				{
					TRACE_PRINTF( "AddIndexDataOnRecord table_name[%s] record_dataunit[%p] index_name[%s] failed[%d]\n" , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->index->index_name , nret );
					db->txn.executed_undo = list_prev_entry_or_null( db->txn.executed_undo , & (db->txn.undo_list) , struct MemUndoData , undo_list_node ) ;
					return nret;
				}
				else
				{
					TRACE_PRINTF( "AddIndexDataOnRecord table_name[%s] record_dataunit[%p] index_name[%s] ok\n" , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->index->index_name );
				}
				break;
			case MDB_UNDO_OPERATION_REMOVE_INDEX_ON_RECORD:
				TRACE_PRINTF( "MDB_UNDO_OPERATION_REMOVE_INDEX_ON_RECORD undo_data[%p] -> DeleteIndexDataOnRecord table_name[%s] record_dataunit[%p] index_name[%s]\n" , db->txn.executed_undo , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->index->index_name );
				nret = DeleteIndexDataOnRecord( db->txn.executed_undo->table , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->index ) ;
				if( nret )
				{
					TRACE_PRINTF( "DeleteIndexDataOnRecord table_name[%s] record_dataunit[%p] index_name[%s] failed[%d]\n" , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->index->index_name , nret );
					db->txn.executed_undo = list_prev_entry_or_null( db->txn.executed_undo , & (db->txn.undo_list) , struct MemUndoData , undo_list_node ) ;
					return nret;
				}
				else
				{
					TRACE_PRINTF( "DeleteIndexDataOnRecord table_name[%s] record_dataunit[%p] index_name[%s] ok\n" , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->index->index_name );
				}
				break;
			default:
				TRACE_PRINTF( "UNKNOW OPERATOR undo_data[%p]\n" , db->txn.executed_undo );
				break;
		}
	}
	
	db->txn.executed_undo = list_last_entry_or_null( & (db->txn.undo_list) , struct MemUndoData , undo_list_node ) ;
	
	return 0;
}

int CancelCurrentTransaction( struct MemDatabase *db )
{
	struct MemUndoData	*prev_undo_data = NULL ;
	
	int			nret = 0 ;
	int			nret2 = 0 ;
	
	TRACE_PRINTF( "db->is_in_txn[%d]\n" , db->is_in_txn );
	if( db->is_in_txn == 1 )
		return 0;
	
	TRACE_PRINTF( "db->txn.executed_undo[%p]\n" , db->txn.executed_undo );
	if( db->txn.executed_undo == NULL )
		return 0;
	
	TRACE_PRINTF( "begin CancelCurrentTransaction\n" );
	
	list_for_each_entry_safe_from_reverse( db->txn.executed_undo , prev_undo_data , & (db->txn.undo_list) , struct MemUndoData , undo_list_node )
	{
		switch( db->txn.executed_undo->operation )
		{
			case MDB_UNDO_OPERATION_ADD_RECORD:
				TRACE_PRINTF( "MDB_UNDO_OPERATION_ADD_RECORD undo_data[%p] -> SetRecordDataUnitUnused table_name[%s] record_dataunit[%p]\n" , db->txn.executed_undo , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit );
				SetRecordDataUnitUnused( db->txn.executed_undo->record_dataunit );
				break;
			case MDB_UNDO_OPERATION_BACKUP_AND_REMOVE_RECORD:
				TRACE_PRINTF( "MDB_UNDO_OPERATION_BACKUP_AND_REMOVE_RECORD undo_data[%p] -> ... table_name[%s] record_dataunit[%p] backup_record_dataunit[%p]\n" , db->txn.executed_undo , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->backup_record_dataunit );
				db->txn.executed_undo->record_dataunit = db->txn.executed_undo->backup_record_dataunit ;
				db->txn.executed_undo->backup_record_dataunit = NULL ;
				break;
			case MDB_UNDO_OPERATION_BACKUP_AND_MODIFY_RECORD:
				TRACE_PRINTF( "MDB_UNDO_OPERATION_BACKUP_AND_MODIFY_RECORD undo_data[%p] -> ... table_name[%s] record_dataunit[%p] backup_record_dataunit[%p] new_record_dataunit[%p]\n" , db->txn.executed_undo , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->backup_record_dataunit , db->txn.executed_undo->new_record_dataunit );
				memcpy( MDB_RECORD_DATAUNIT_TO_DATA(db->txn.executed_undo->record_dataunit) , MDB_RECORD_DATAUNIT_TO_DATA(db->txn.executed_undo->backup_record_dataunit) , db->txn.executed_undo->table->record_size );
				break;
			case MDB_UNDO_OPERATION_ADD_INDEX_ON_RECORD:
				TRACE_PRINTF( "MDB_UNDO_OPERATION_ADD_INDEX_ON_RECORD undo_data[%p] -> DeleteIndexDataOnRecord table_name[%s] record_dataunit[%p] index_name[%s] ...\n" , db->txn.executed_undo , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->index->index_name );
				nret = DeleteIndexDataOnRecord( db->txn.executed_undo->table , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->index ) ;
				if( nret )
				{
					TRACE_PRINTF( "DeleteIndexDataOnRecord table_name[%s] record_dataunit[%p] index_name[%s] failed[%d]\n" , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->index->index_name , nret );
					nret2 = nret ;
				}
				else
				{
					TRACE_PRINTF( "DeleteIndexDataOnRecord table_name[%s] record_dataunit[%p] index_name[%s] ok\n" , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->index->index_name );
				}
				break;
			case MDB_UNDO_OPERATION_REMOVE_INDEX_ON_RECORD:
				TRACE_PRINTF( "MDB_UNDO_OPERATION_REMOVE_INDEX_ON_RECORD undo_data[%p] -> AddIndexDataOnRecord table_name[%s] record_dataunit[%p] index_name[%s] ...\n" , db->txn.executed_undo , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->index->index_name );
				nret = AddIndexDataOnRecord( db->txn.executed_undo->table , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->index ) ;
				if( nret )
				{
					TRACE_PRINTF( "AddIndexDataOnRecord table_name[%s] record_dataunit[%p] index_name[%s] failed[%d]\n" , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->index->index_name , nret );
					nret2 = nret ;
				}
				else
				{
					TRACE_PRINTF( "AddIndexDataOnRecord table_name[%s] record_dataunit[%p] index_name[%s] ok\n" , db->txn.executed_undo->table->table_name , db->txn.executed_undo->record_dataunit , db->txn.executed_undo->index->index_name );
				}
				break;
			default:
				TRACE_PRINTF( "UNKNOW OPERATOR undo_data[%p]\n" , db->txn.executed_undo );
				break;
		}
	}
	
	return nret2;
}

int DestroyCurrentTransaction( struct MemDatabase *db )
{
	struct MemUndoData	*next_undo_data = NULL ;
	
	TRACE_PRINTF( "db->is_in_txn[%d]\n" , db->is_in_txn );
	if( db->is_in_txn == 1 )
		return 0;
	
	TRACE_PRINTF( "begin DestroyCurrentTransaction\n" );
	
	db->txn.txn_no = 0 ;
	list_for_each_entry_safe( db->txn.executed_undo , next_undo_data , & (db->txn.undo_list) , struct MemUndoData , undo_list_node )
	{
		SetUndoDataUnused( db->txn.executed_undo );
	}
	db->txn.executed_undo = NULL ;
	
	return 0;
}

int MDBBeginTransaction( struct MemDatabase *db )
{
	db->is_in_txn = 0 ;
	
	CreateCurrentTransaction( db );
	
	db->is_in_txn = 1 ;
	
	return 0;
}

int MDBCommitTransaction( struct MemDatabase *db )
{
	db->is_in_txn = 0 ;
	
	DestroyCurrentTransaction( db );
	
	return 0;
}

int MDBRollbackTransaction( struct MemDatabase *db )
{
	db->is_in_txn = 0 ;
	
	CancelCurrentTransaction( db );
	
	return 0;
}

